iT邦幫忙

2022 iThome 鐵人賽

DAY 2
0
自我挑戰組

RISCV 作業系統與處理器 淺談系列 第 2

在作業系統實作中會用到的c語言 Part1

  • 分享至 

  • xImage
  •  

大綱

這裡需要先假設讀者已經大概知道一般c語言基礎語法,以下會概述我在學習中碰到的有趣新知識!

  • Pointer to Pointer
  • functional pointer
  • Macro

Pointer to Pointer(指標的指標)

學習c的時候,許多人會對指標避之唯恐不及,但是在研究作業系統時,它卻是避不開的第一關。
如果還對指標不熟,可以先看看C語言: 超好懂的指標,初學者請進~
接下來,我們來看一個使用pointer to pointer的例子(來自jserv老師你所不知道的C語言:linked list 和非連續記憶體

void remove_list_node(List *list, Node *target)
{
    // The "indirect" pointer points to the *address*
    // of the thing we'll update.
    Node **indirect = &list->head;
    // Walk the list, looking for the thing that 
    // points to the node we want to remove.
    while (*indirect != target)
        indirect = &(*indirect)->next;
    *indirect = target->next;
}

我是這樣理解的:(先說明,pointer我都想成指向一個特定結構的地址,例 int *a; a = the address of an interger)

  1. indirect = the address of a pointer to an node structure(指標的地址)
  2. *indirect = the value pointed by the address of the integer = the address of an node structure
  3. **indirect = the value of an node sturcture

所以,list->head本身的值是一個node的地址,而&list_head就是此指標的地址。
indirect中的值為此指標所儲存的值,即一個node的地址。
所以當
indirect中node的地址不等於target所儲存node的地址,我們把indirect所儲存指標的位址換下一個指標位址!

如此一來,程式會變得更精簡,因為不須考慮移除node為第一個或最後一個的特殊案例!
這有沒有很神奇!我寫的可能還是太雜亂,建議自己畫圖理解一次會比較好!

Functional Pointer

在寫c語言,如果使用qsort等內建函式,如何理解最後一個參數(compare fucntion)?

以下截取自Linux Programmar's Manual

void qsort(void *base, size_t nmemb, 
            size_t size,int (*compar)(const void *, const void *));

其中,compar是一個函式,輸入值為兩個const void * ,而輸出為一個integer。

接著,我們再來看更複雜一點的例子!

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

我們先看,typedef中,我們定義了sighandler_t這一個函式,輸入為一個integer,輸出為void。
最後,我們了解signal這個函式,輸入有兩個,一個integer和一個sighandler_t,而輸出也是一個sighander_t的函式!
有沒有覺得有趣呢!我剛學到的時候,有重燃學程式的欲望/images/emoticon/emoticon24.gif

Macro

我們在編譯(compile)一個c程式(program),除了直接取得目的檔(object file),我們的gcc(是一個compiler driver之後會詳述)其實中間做了許多事呢!其中一個就是前置處理(preproccess),詳細敘述請參考jserv老師的你所不知道的 C 語言:前置處理器應用篇
而我們的macro(巨集),就是在此一階段被展開,讓我們的程式更精簡且具更高擴充性!

注意!這裡我們必須知道還有一個叫gcc attribute的東西,跟macro是不一樣的喔!gcc attribute是給gcc編譯時所看的東西,以下先舉一個gcc attribute的小例子

struct __attribute__ ((__packed__)) my_node {
    int val;
    char name;
};

在這裡,attribute ((packed)) 是告訴gcc不要內存對齊!什麼是內存對齊(alignment)?就是讓儲存最小單位固定(一般預設長度為4bytes),以達到cpu儲存變數的速度加快(為什麼之後在cpu結構介紹會了解的!)。
在以上的例子中,若有packed,則sizeof(my_node) = 8,若無,則sizeof(my_node) = 5。

好了,我們回到macro,先講一個簡單的東西: do{}while(0)
以下為例子:

#define foo() do{\
                func1();\
                func2();\
          }while(0)
int main(){
    foo();
    return 0;
}

在寫多個語句時,為了避免展開時造成混亂,我們必須先加上這個看似不起任何作用的do...while,注意,在while後面不要加上";",展開後為以下(使用gcc -E)

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"

int main(){
    do{ func1(); func2(); }while(0);
    return 0;
}

結語

今天先寫到這,明天繼續講macro和其他c語言的議題~


上一篇
Day1 為什麼寫這文章?寫什麼?
下一篇
在作業系統實作中會用到的c語言 Part2
系列文
RISCV 作業系統與處理器 淺談4
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言